#include "grid.h"

Grid::Grid(const char *name) {
	this->name = new char[strlen(name)+1];
	this->name = strcpy((char *)this->name, name);
	this->sites = NULL;
	this->links = NULL;
	this->routes = NULL;
	this->numberOfSites = 0;
	this->numberOfLinks = this->numberOfSites - 1;
	this->numberOfRoutes = this->numberOfSites;
}

Grid::~Grid() {
	if (this->name) {
		delete this->name;
		this->name = NULL;
	}
	
	for (int n = 0; n<this->numberOfLinks; n++) {
		if (this->links[n] != NULL) {
			delete this->links[n];			
			this->links[n] = NULL;
		}
	}
	free(this->links);
	this->links = NULL;

	for (int m = 0; m<this->numberOfSites; m++) {
		if (this->sites[m] != NULL) {
			delete this->sites[m];
			this->sites[m] = NULL;
		}
	}
	free(this->sites);
	this->sites = NULL;
}

void Grid::addSite(const char *site_domain, long double intralink_bw, long double interlink_lat, long double interlink_bw, long double interlink_bw_lim) {

	this->numberOfSites++;
	this->numberOfLinks++;
	this->numberOfRoutes++;	
	
	/*Reallocate the memory used by the array of sites and routes*/
	this->sites = (Site **) realloc(this->sites, this->numberOfSites*sizeof(Site *));
	this->routes = (SG_Resource *) realloc(this->routes, this->numberOfRoutes*sizeof(SG_Resource));		
	/*Instantiate the new site. The site[0] is considered the "base site"*/
	this->sites[numberOfSites - 1] = new Site(site_domain, intralink_bw);
	
	/*Instantiate the new route according to the current number of sites*/	
	char *route_name = new char[80];
	SG_Resource route_links[4];	
	
	if (numberOfSites>1)  { /*There are at least two sites*/
		/*Reallocate the memory used by the array of links*/
		this->links = (Link **) realloc(this->links, this->numberOfLinks*sizeof(Link *));
		/*Instantiate the new interlink*/
		char *link_name = new char[80];
		sprintf(link_name, "interlink_%s_%s", sites[0]->getDomain(), sites[numberOfSites - 1]->getDomain());			
		this->links[numberOfLinks-1] = new Link(link_name, interlink_lat,  interlink_bw, interlink_bw_lim);
		delete link_name;
		/*Instantiate the new route*/
		sprintf(route_name, "route_%s_%s", sites[0]->getDomain(), sites[numberOfSites - 1]->getDomain());			
		route_links[0] = sites[0]->getLink()->getSGResource();
		route_links[1] = links[numberOfLinks-1]->getSGResource();			
		route_links[2] = sites[numberOfSites - 1]->getLink()->getSGResource();			
		route_links[3] = NULL;			
		
	} else { /*There is only a single site*/
		sprintf(route_name, "internalroute_%s", sites[0]->getDomain());
		route_links[0] = this->sites[numberOfRoutes -1]->getLink()->getSGResource();
		route_links[1] = NULL;
		route_links[2] = NULL;
		route_links[3] = NULL;				

	}
	
	this->routes[numberOfRoutes -1] = SG_newTCPRoute(route_name, route_links, NULL);
	delete route_name;
	////////////////////////////////////////////////////////

}

void Grid::addSite(const char *site_domain, const char *intralink_bw, long double intralink_bw_offset,  const char *interlink_lat, long double interlink_lat_offset, const char *interlink_bw, long double interlink_bw_offset, const char *interlink_bw_lim, long double interlink_bw_lim_offset, const char *intralink_bw_error, const char *interlink_lat_error, const char *interlink_bw_error, const char *intralink_bw_forecast, const char *interlink_lat_forecast, const char *interlink_bw_forecast, const char *interlink_bw_lim_forecast) {
	
	if ( ((interlink_lat_forecast!=NULL) && FileChecker::verify(interlink_lat_forecast)) || ((interlink_bw_lim_forecast!=NULL) && FileChecker::verify(interlink_bw_lim_forecast)) || ((interlink_bw_forecast!=NULL) && FileChecker::verify(interlink_bw_forecast)) || ((intralink_bw_forecast!=NULL) && FileChecker::verify(intralink_bw_forecast)) || FileChecker::verify(intralink_bw) || ( (this->numberOfSites>0) && FileChecker::verify(interlink_lat)) && FileChecker::verify(interlink_bw) && FileChecker::verify(interlink_bw_lim) ) {
		throw std::runtime_error("Cannot open intralink or/and interlink trace file");		
	}

	this->numberOfSites++;
	this->numberOfLinks++;
	this->numberOfRoutes++;	
	
	/*Reallocate the memory used by the array of sites and routes*/
	this->sites = (Site **) realloc(this->sites, this->numberOfSites*sizeof(Site *));
	this->routes = (SG_Resource *) realloc(this->routes, this->numberOfRoutes*sizeof(SG_Resource));		
	/*Instantiate the new site. The site[0] is considered the "base site"*/
	this->sites[numberOfSites - 1] = new Site(site_domain, intralink_bw, intralink_bw_offset, intralink_bw_error, intralink_bw_forecast);
	
	/*Instantiate the new route according to the current number of sites*/	
	char *route_name = new char[80];
	SG_Resource route_links[4];	
	
	if (numberOfSites > 1)  { /*There are at least two sites*/
		/*Reallocate the memory used by the array of links*/
		this->links = (Link **) realloc(this->links, this->numberOfLinks*sizeof(Link *));
		/*Instantiate the new interlink*/
		char *link_name = new char[80];
		sprintf(link_name, "interlink_%s_%s", sites[0]->getDomain(), sites[numberOfSites - 1]->getDomain());			
		
		this->links[numberOfLinks-1] = new Link(link_name, interlink_lat, interlink_lat_offset, interlink_bw, interlink_bw_offset, interlink_bw_lim, interlink_bw_lim_offset, interlink_lat_error, interlink_bw_error, interlink_lat_forecast, interlink_bw_forecast, interlink_bw_lim_forecast);
		
		delete link_name;
		/*Instantiate the new route*/
		sprintf(route_name, "route_%s_%s", sites[0]->getDomain(), sites[numberOfSites - 1]->getDomain());			
		route_links[0] = sites[0]->getLink()->getSGResource();
		route_links[1] = links[numberOfLinks-1]->getSGResource();			
		route_links[2] = sites[numberOfSites - 1]->getLink()->getSGResource();			
		route_links[3] = NULL;			
		
	} else { /*There is only a single site*/
		sprintf(route_name, "internalroute_%s", sites[0]->getDomain());
		route_links[0] =this->sites[numberOfRoutes -1]->getLink()->getSGResource();
		route_links[1] = NULL;
		route_links[2] = NULL;
		route_links[3] = NULL;				

	}
	
	this->routes[numberOfRoutes -1] = SG_newTCPRoute(route_name, route_links, NULL);
	delete route_name;
	////////////////////////////////////////////////////////
}

const char *Grid::getName() {
	return this->name;
}

int Grid::getNumberOfSites() {
	return this->numberOfSites;
}

int Grid::getNumberOfLinks() {
	return this->numberOfLinks;
}

int Grid::getNumberOfRoutes() {
	return this->numberOfRoutes;
}

Link *Grid::getLinkByIndex(int index) {
	if  ( (numberOfLinks<0) ||(index<0) || (index >= this->numberOfLinks)) throw std::runtime_error("invalid link index");	
	return this->links[index];
}

Site *Grid::getSiteByIndex(int index) {
	if  ((index<0) || (index >= this->numberOfSites)) throw std::runtime_error("invalid site index");
	return this->sites[index];
}

Site *Grid::getSiteByDomain(const char *site_domain) {
	Site *site= NULL;
	for (int n=0; n<this->numberOfSites; n++) {
		if (!strcmp(site_domain, this->getSiteByIndex(n)->getDomain())) {
			site = this->getSiteByIndex(n);
		}
	}
	if (site==NULL) throw std::runtime_error("Site does not exist in the Grid");	
	return site;
}

SG_Resource Grid::getRouteToSite(int site_index) {
	if  ((site_index<0) || (site_index >= this->numberOfSites)) throw std::runtime_error("invalid site index");
	return this->routes[site_index];
}

Link *Grid::getBottleneckLinkOfRouteToSite(int site_index) {
	//In this model, a route between two sites is composed by 3 links: 2 intra-site and 1 inter-site
	if ( (site_index<0) || (site_index >= this->numberOfSites))
		throw std::runtime_error("Invalid site index!");
	
	//The initial link
	Link *bottleneck = this->getSiteByIndex(0)->getLink();
	
	if (site_index > 0)	 {
		// A inter-site link "n-1" corresponds to a link that interconects the site 0 (base site) to site "n"
		Link *link = this->getLinkByIndex(site_index - 1);
		if (link->getBandwidthAvailable(-1) < bottleneck->getBandwidthAvailable(-1)) {
			bottleneck = link;
		}
		
		link = this->getSiteByIndex(site_index)->getLink();
		if (link->getBandwidthAvailable(-1) < bottleneck->getBandwidthAvailable(-1)) {
			bottleneck = link;			
		}
	}

	return bottleneck;
}

bool Grid::isTotallyBusy() {
	bool busy = true;
	for (int n = 0; n< this->getNumberOfSites(); n++) {
		if (!this->getSiteByIndex(n)->allProcessorsAreBusy()) {
			busy = false;
			break;
		}
	}
	
	return busy;
}

int Grid::getTotalNumberOfProcessors() {
	int total = 0;
	for (int s = 0; s<this->numberOfSites; s++) {
		total+=this->getSiteByIndex(s)->getNumberOfProcessors();
	}
	return total;
}

int Grid::getNumberOfAllocatedTasks() {
	int numberOfTasks = 0;;
	for (int s = 0; s < this->numberOfSites; s++) {
		numberOfTasks+=this->getSiteByIndex(s)->getNumberOfAllocatedTasks();
	} 
	return numberOfTasks;
}

void Grid::updateOffsetOfAllResources(long double new_offset) {
	//Update the interlinks
	for (int l = 0; l < this->getNumberOfLinks(); l++) {
		Link *link = this->getLinkByIndex(l);

		link->setLatencyOffset(link->getLatencyOffset() + new_offset);
		link->setBandwidthOffset(link->getBandwidthOffset() + new_offset);		
		link->setBandwidthLimitOffset(link->getBandwidthLimitOffset() + new_offset);		
	}
	
	for (int s = 0; s < this->getNumberOfSites(); s++) {
		Site *site = this->getSiteByIndex(s);

		//Update the processors
		for (int p = 0; p< site->getNumberOfProcessors(); p++) {
			Processor *proc = site->getProcessorByIndex(p);
			proc->setCpuOffset(proc->getCpuOffset() + new_offset);
		}
		//Update the intralinks
		Link *link = site->getLink();

		link->setLatencyOffset(link->getLatencyOffset() + new_offset);
		link->setBandwidthOffset(link->getBandwidthOffset() + new_offset);		
		link->setBandwidthLimitOffset(link->getBandwidthLimitOffset() + new_offset);		
	}
}

Processor *Grid::getProcessorByName(const char *proc_name) {
	Processor *proc = NULL;
	for(int i = 0; i < this->numberOfSites; i++) {
		Site *site = this->getSiteByIndex(i);
		try {
			proc = site->getProcessorByName(proc_name);
			break;
		} catch(std::runtime_error &e) {}
	}
	if(proc == NULL) throw std::runtime_error("Processor does not exist in this grid");
	return proc;	
}

bool Grid::allSitesWithAllAvailableInformationAreBusy() {
	bool busy = true;
	for (int n = 0; n< this->getNumberOfSites(); n++) {
		if (this->getSiteByIndex(n)->hasAllAvailableInformation()) {
			
			if (!this->getSiteByIndex(n)->allProcessorsAreBusy()) {
				busy = false;
				break;
			}
		}
	}
	
	return busy;
}


